package engine.data;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import misc.Constant;
import misc.Debug;
import misc.PersistenceTools;
import misc.Persistent;
import util.ArrayList;
import util.List;
import engine.UnitAction;
import engine.controllers.AIController;
import engine.controllers.HumanController;
import engine.controllers.PlayerController;

public class Player extends Persistent {

	public static final byte HUMAN = 0;

	public static final byte COMPUTER = 1;

	public static final byte NET_HUMAN = 2;

	public byte civilization;

	public List units = new ArrayList(Constant.INIT_PLAYER_UNITS_SIZE);

	public List cities = new ArrayList(Constant.INIT_PLAYER_CITIES_SIZE);

	public List technologies = new ArrayList(Constant.INIT_PLAYER_TECHNOLOGIES_SIZE);

	public byte technology = Technology.NONE;

	public boolean showOnScr;

	public short knowledgePoints;

	public short currCityIndex;

	public boolean played;

	public byte type;

	public byte taxRate;

	public byte luxuryRate;

	public byte knowledgeRate;

	// for territories (after 16 cities) : static because every players use this
	public static byte currTerritoryIndex = 0;

	public Unit currUnit;

	public short currUnitIndex = 0;

	private static byte currId = 0;

	public PlayerController controller;

	public byte government = GovernmentType.ANARCHY;

	public Player(final byte playerType, final byte civId, final boolean showOnScreen) {
		if (playerType == COMPUTER)
			controller = new AIController(this);
		else if (playerType == HUMAN)
			controller = new HumanController(this);
		this.setId(currId);
		this.type = playerType;
		this.taxRate = Constant.INIT_TAX_RATE;
		this.knowledgeRate = Constant.INIT_KNOWLEDGE_RATE;
		this.luxuryRate = Constant.INIT_LUXURY_RATE;
		this.civilization = civId;
		this.showOnScr = showOnScreen;
		currId++;
	}


	public void beginTurn() {
		this.played = false;
		this.knowledgePoints += getRP();
		Unit unit;
		City city;
		for (byte i = 0; i < this.units.size(); i++) {
			unit = (Unit) this.units.get(i);
			unit.beginTurn();
		}
		for (byte i = 0; i < this.cities.size(); i++) {
			city = (City) this.cities.get(i);
			city.beginTurn();
		}
		this.currUnit = null;
	}

	public void addCity(final City city) {
		this.cities.add(city);
		this.currCityIndex++;
	}

	public void addUnit(final Unit unit) {
		this.units.add(unit);
	}

	public void removeUnit(final Unit unit) {
		this.units.remove(unit);
		this.currUnitIndex--;
	}

	public void removeUnits(final List deadUnits) {
		int size = deadUnits.size();
		for (int i = size - 1; i >= 0; i--)
			removeUnit((Unit) deadUnits.get(i));
	}

	public void removeCity(final City city) {
		this.cities.remove(city);
		this.currCityIndex--;
	}

	public boolean nextUnit() {
		Debug.logConsole("a - " + this.currUnit);
		if (this.units.size() == 0)
			return false;
		if (this.currUnit == null) {
			this.currUnitIndex = 0;
			this.currUnit = (Unit) this.units.get(this.currUnitIndex);
			if (!this.currUnit.isUnplayable() && !this.currUnit.waiting && this.currUnit.action == UnitAction.NONE)
				return true;
		}
		Debug.logConsole("b - " + this.currUnit);
		short index = this.currUnitIndex;
		//incUnitIndex();
		Debug.logConsole("c - " + this.currUnit);
		while (this.currUnitIndex != index
				&& (this.currUnit.isUnplayable() || this.currUnit.waiting || this.currUnit.action != UnitAction.NONE)) {
			// TODO : prendre en compte les bonus de fortification et autres ici
			incUnitIndex();
			Debug.logConsole("d - " + this.currUnit);
		}
		if (this.currUnitIndex == index) {
			incUnitIndex();
		 Debug.logConsole("e - " + this.currUnit);
			while (this.currUnitIndex != index && this.currUnit.isUnplayable() && this.currUnit.waiting) {
				if (this.currUnit.waiting)
					this.currUnit.waiting = false;
				else
					incUnitIndex();
				 Debug.logConsole("f - " + this.currUnit);
			}
			if (this.currUnit.isUnplayable() && !this.currUnit.waiting)
				return false;
		}
		return true;
	}

	public short getRP() {
		short rp = 0;
		City city;
		for (short i = 0; i < this.cities.size(); i++) {
			city = (City) this.cities.get(i);
			rp += city.getKnowledge(this.knowledgeRate);
		}
		return rp;
	}

	public short getMaxRP(final byte tech) {
		if (tech == Technology.NONE)
			return 0;
		// 6 Chief , 8 Warlord , 10 Prince , 12 King ,14 Emperor
		short difficultyModifier = 6;
		// 1 if Year <= 0AD, or, 2 if Year > 0AD
		short timeModifier = 1;
		List previousAdvances = Technology.getPreviousAdvances(tech);
		if (previousAdvances != null)
			return (short) (10 + (previousAdvances.size() * difficultyModifier * timeModifier));
		return 10;
	}

	private void incUnitIndex() {
		this.currUnitIndex = (short) ((this.currUnitIndex + 1) % this.units.size());
		this.currUnit = (Unit) this.units.get(this.currUnitIndex);
	}
	
	public void persist(final DataOutputStream dout, final boolean complete) throws IOException {
		if (complete) {
			PersistenceTools.persistList(this.units, dout, true);
			PersistenceTools.persistList(this.cities, dout, true);
			dout.writeByte(this.civilization);
			dout.writeShort(this.currCityIndex);
			PersistenceTools.persist(currUnit, dout, false);
			dout.writeShort(this.currUnitIndex);
			dout.writeByte(this.government);
			dout.writeByte(this.luxuryRate);
			dout.writeBoolean(this.played);
			dout.writeByte(this.knowledgePoints);
			dout.writeByte(this.knowledgeRate);
			dout.writeBoolean(this.showOnScr);
			dout.writeByte(this.taxRate);
			PersistenceTools.persistByteList(this.technologies, dout);
			dout.writeByte(this.technology);
			dout.writeByte(this.type);
		}
	}

	public void resurrect(final DataInputStream din, final boolean complete) throws IOException {
		if (complete) {
			this.units = PersistenceTools.resurrectUnitList(din, true);
			this.cities = PersistenceTools.resurrectCityList(din, true);
			this.civilization = din.readByte();
			this.currCityIndex = din.readShort();
			currUnit = PersistenceTools.resurrectUnit(din, false);
			this.currUnitIndex = din.readShort();
			this.government = din.readByte();
			this.luxuryRate = din.readByte();
			this.played = din.readBoolean();
			this.knowledgePoints = din.readByte();
			this.knowledgeRate = din.readByte();
			this.showOnScr = din.readBoolean();
			this.taxRate = din.readByte();
			this.technologies = PersistenceTools.resurrectByteList(din);
			this.technology = din.readByte();
			this.type = din.readByte();
			if (this.type == Player.COMPUTER)
				this.controller = new AIController(this);
			if (this.type == Player.HUMAN)
				this.controller = new HumanController(this);
		}
	}

	public void linkPlayer() {
		if (this.units != null)
			PersistenceTools.linkUnitList(this);
		if (this.currUnit != null)
			this.currUnit.player = this;
		if (this.cities != null)
			PersistenceTools.linkCityList(this);
	}

}
